/**
 * 
 */
package easyservice.application;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import easyservice.network.ServerNetworkAgent;
import easyservice.network.XMPPServerNetworkAgent;
import easyservice.protocol.Command;
import easyservice.protocol.FaultCommand;
import easyservice.protocol.NotifyCommand;
import easyservice.protocol.RequestCommand;
import easyservice.protocol.ResponseCommand;

/**
 * Default implementation of ServerAgent interface.
 * 
 * @author Rodrigo Barbosa Lira
 * 
 */
public class ServerAgentImpl<T, I> implements ServerAgent<T, I> {
	
	private static Log log = LogFactory.getLog(ServerAgentImpl.class);

	private ServerNetworkAgent networkAgent;
	
	private Class<I> notifiableInterface;
	
	private T facade;

	public ServerAgentImpl(String username, String password, String hostUrl, int hostPort, T facade, Class<I> notifiableInterface) {
		networkAgent = new XMPPServerNetworkAgent(username, password, hostUrl, hostPort, this);
		this.notifiableInterface = notifiableInterface;
		this.facade = facade;
	}

	/*(non-Javadoc)
	 * @see easyservice.network.NetworkListener#onReceiveMessage(easyservice.protocol.Command)
	 */
	@Override
	public Command onReceiveMessage(Command command) {
		RequestCommand request = (RequestCommand) command;
		log.debug("Request Command. Method: " + request.getMethod());
		
		String result = null;
		try {
			log.info("Call method in facade and receive response");
			result = String.valueOf(MethodUtils.invokeMethod(this.facade, request.getMethod(), request.getParams().toArray()));
		} catch (Throwable e) {
			log.error("Error", e);
			return new FaultCommand(e.getClass().toString(), e.getMessage());
		}
//		} catch (NoSuchMethodException e) {
//			log.error("Method not found", e);
//			throw new EasyServiceException(e);
//		} catch (IllegalAccessException e) {
//			log.error("Illegal Access", e);
//			throw new EasyServiceException(e);
//		} catch (InvocationTargetException e) {
//			log.error("Invocation Target", e);
//			throw new EasyServiceException(e);
//		}
		log.info("Returning result");
		return new ResponseCommand(result);
	}

	/* (non-Javadoc)
	 * @see easyservice.application.ServerAgent#getNotifiableInterface()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public I getNotifiableInterface() {
		Class[] interfaces = {this.notifiableInterface};
		return (I) Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, new ClientProxy(networkAgent));
	}
}

class ClientProxy implements InvocationHandler {
	
	private ServerNetworkAgent networkAgent;
	
	public ClientProxy(ServerNetworkAgent networkAgent) {
		this.networkAgent = networkAgent;
	}

	/* (non-Javadoc)
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// to notify all clients

		String nameOfMethod = method.getName();
		List<String> params = new ArrayList<String>();
		
		if (args != null) {
			for (Object o : args) {
				params.add(String.valueOf(o));
			}
		}
		
		Command command = new NotifyCommand(nameOfMethod, params);
		networkAgent.sendMessageToClients(command.generatedString());
		
		// return nothing
		return null;
	}
}